﻿<template>
    <div class="image-block" v-if="hideOnEmpty != true || disabled != true || photoList.length > 0">
        <!-- <div v-if="photoList.length == 1 && max == 1" v-bind="$attrs">
            <MyImg :src="realPhoto" fit="contain" :style="{ width: imgSize, height: imgSize }"
                :preview-src-list="[getImgUrl(modelValue)]">
                <template #error>
                    <div class="image-slot">
                        <el-icon class="pic-icon">
                            <Picture />
                        </el-icon>
                    </div>
                </template>
            </MyImg>

            <span v-if="!disabled" v-on:click="removeImage(modelValue)" class="img-remove-btn">
                <el-icon :size="16">
                    <Close />
                </el-icon>
            </span>
        </div> -->
        <div class="my-image-list">
            <span v-for="img in photoList" :class="isVideo(img)?'filebox':'my-image-item'">
                <video v-if="isVideo(img)" :src="getImgUrl(img)" controls="controls" class="filebox" style="aspect-ratio:4/3;"></video>
                <MyImg v-else class="my-image-item" v-bind:src="getThumbUrl(img)" :preview-src-list="[getImgUrl(img)]" fit="contain"
                    :style="{ width: imgSize, height: imgSize }" :z-index="5000">
                    <template #error>
                        <!-- 如果找不到缩略图，就用原图-->
                        <MyImg v-bind:src="getImgUrl(img)" fit="contain" :style="{ width: imgSize, height: imgSize }"
                            :z-index="5000">
                            <template #error>
                                <!-- 如果找不到原图，就显示-->
                                <div class="image-slot">
                                    <el-icon class="pic-icon">
                                        <Picture />
                                    </el-icon>
                                </div>
                            </template>
                        </MyImg>
                    </template>
                </MyImg>
                <span v-if="!disabled" v-on:click="removeImage(img)" class="img-remove-btn">
                    <el-icon :size="24">
                        <CloseBold />
                    </el-icon>
                </span>
            </span>
            <div v-if="!disabled && photoList.length < max" v-on:click="doUpload" style="display: inline-block;">
                <!-- <el-button v-if="isMiniProgram" :disabled="percent != 0" type="default" v-on:click="uploadWechatFile">+聊天图片</el-button> -->
                <el-icon class="pic-icon" v-if="percent == 0">
                    <Plus />
                </el-icon>
                <el-progress v-else type="circle" :percentage="percent" :width="imgWidth" class="pic-icon" />
            </div>
            <div v-if="photoList.length == 0 && disabled" style="display: inline-block;">
                <el-icon class="pic-icon">
                </el-icon>
            </div>
        </div>
        <input type="file" :accept="accept" :multiple="allowMulti" ref="inputRef" v-on:change="handleChange"
            style="display:none" />
        <input type="file" accept="image/*" capture="camera" ref="inputCameraRef" v-on:change="handleChange"
            style="display:none" />
        <input type="file" accept="video/*" capture="environment" ref="inputVideoRef" v-on:change="handleChange"
            style="display:none" />
    </div>
</template>

<script>
import store from './store.js';
import zoomImage from './zoomImage'
import { Plus, Close, Picture, CloseBold } from '@element-plus/icons-vue'

export default {
    name: 'ImageUploader',
    emits: ['update:modelValue', "uploadOK", "remove"],
    components: { Plus, Close, Picture },
    props: {
        modelValue: {
            type: [String],
            default: '',
        },
        formData:Object,
        disabled: Boolean,
        extData: Object,
        server: String,
        ossServer: Boolean,
        headers: {
            type: Object,
            default: function () { return {} },
        },
        max: {
            type: Number,
            default: 9
        },
        maxSize: {
            type: Number,
            default: 50
        },
        size: {
            type: Number,
            default: 50
        },
        resize: {
            //上传文件最大尺寸（最大高度或最大宽度， 0表示不需要缩放）
            type: Number,
            default: 2880
        },
        showThumb: {
            type: Boolean,
            default: true
        },
        imgSize: {
            type: [Number, String],
            default: "100px",
        },
        onlyCamera: Boolean,
        onlyVideo: Boolean,
        accept:{
            type:String,
            default:"image/*"
        },
        hideOnEmpty: Boolean,
        beforeUpload:Function,
    },
    data: function () {
        return {
            percent: 0,
            isMiniProgram: false
        }
    },

    mounted() {
        this.percent = 0
        this.checkWechatEnvironment()
    },
    methods: {
        checkWechatEnvironment() {
            // 检查是否在微信环境中
            const ua = navigator.userAgent.toLowerCase()
            this.isMiniProgram = ua.indexOf('miniprogram') !== -1 
                && typeof wx !== 'undefined'
                && wx.miniProgram  !== 'undefined'
                && window.miniProgramHybrid !== 'undefined';
        },
        uploadWechatFile() {
            if (!this.isMiniProgram) return
            let postUrl = this.getUrl(this.server || store.UploadServer)
            postUrl = location.protocol + "//" + location.hostname + ":" + location.port + postUrl
            const param = {
                url: location.href,
                type:"image",
                post_url:  postUrl,
                pm:{
                    ...this.extData, 
                    toOss: this.ossServer?1:0
                },
                header: this.getHeader()
            }
            miniProgramHybrid.call("uploadFile", param).then(res => {
                if (!res.success) {
                    alert(res.message)
                    return
                }
                alert("uploadFile:"+JSON.stringify(res));

                this.$emit("uploadOK", { url: res.url, name: res.name });
                var item = { name: res.name, type: res.type, size: res.size, url: res.url, icon: "" }
                
                var item = this.modelValue || "";
                var url = res.url;
                if (url.endsWith(".thumb.jpg")) {
                    url = url.substr(0, url.length - 10);
                }
                item += url;
                this.$emit('update:modelValue', item)
            })
        },
        isVideo(fn){
            let ix = fn.lastIndexOf(".");
            if(ix>-1){
                let ext = fn.substring(ix).toLowerCase();
                return [".mp4", ".mov", ".avi", ".m4v", ".mpeg",".ogm",".wmv",".mpg",".webm",".m4v",".asx"].includes(ext);
            }
            return false;
        },
        getThumbUrl(url) {
            if (this.showThumb) {
                return store.getUrl(url) + ".thumb.jpg";
            }
            return store.getUrl(url);
        },

        getImgUrl(url) {
            return store.getUrl(url);
        },
        removeImage: function (url) {
            var that = this
            var item = that.modelValue || "";
            var imgList = item.split(/[;,|\n]/g).filter(s => s != '')
            var j = imgList.indexOf(url)
            imgList.splice(j, 1)
            item = imgList.join(';')
            that.$emit('update:modelValue', item)
            that.$emit("remove", url);
        },

        handleChange: function (e) {
            const files = e.target.files;
            if (!files) return;
            let allowLen = Math.min(files.length, this.max - this.photoList.length);
            if (allowLen > 0) {
                var ff = [];
                for (var i = 0; i < allowLen; i++) {
                    ff[i] = files[i];
                }
                this.uploadFiles(ff);
            }
        },

        uploadFiles: function (files) {
            for (var i = 0; i < files.length; i++) {
                var file = files[i]
                //屏蔽文件类型检查，可能最新的华为手机或小米手机，不一定会返回正确的type
                // if (file.type != "image/jpeg" && file.type != "image/png" && file.type != "image/gif" && file.type != "image/webp") {
                //     $msg.message("只支持图片文件类型","error")
                //     return
                // }
                if(this.onlyVideo && !this.isVideo(file.name)){
                    $msg.message("不支持的视频类型");
                    return;
                }
                if (file.size > this.maxSize2 * 1024 * 1024) {
                    $msg.message(`最大支持${this.maxSize2}M大小的文件`, "error")
                    return
                }

            }
            if (files.length > 0) {
                this.uploadOne(files[0]).then(res => {
                    if (files.length > 1) {
                        files.splice(0, 1);
                        this.uploadFiles(files);
                    }
                });
            }
        },
        
        async uploadOne(file) {
            if (this.beforeUpload) {
                try {
                    const processedFile = await Promise.resolve(this.beforeUpload(file));
                    file = processedFile || file;
                } catch (err) {
                    file = file;
                }
            }

            if (this.resize > 0 && file.type.startsWith("image/") && file.type != "image/gif") {
                //如果指定了最大宽高，则缩放以后再上传
                var formData = new FormData();
                return zoomImage(file, formData, this.resize, null, store.supportWebp).then(res => {
                    let file = formData.get("file");
                    return this.postFile(file, null, file.name);
                })
            } else {
                //无需缩放||视频文件
                return this.postFile(file, null, file.name);
            }
        },

        postFile: function (rawFile, formData, fileName) {
            if (this.ossServer) {
                let pm = {
                    ...this.extData,
                    filename:fileName
                }
                //向服务器查询是否可以接受的文件
                return $server.call("/Common/OssSignature", pm).then(res => {
                    //上传到OSS服务器上
                    var data = [];
                    data['key'] = 'NSO' + res.key;
                    data['OSSAccessKeyId'] = res.OSSAccessKeyId;
                    data['policy'] = res.policy;
                    data['signature'] = res.signature;
                    data['success_action_status'] = '200';
                    data['Content-Disposition'] = 'attachment;filename="' + fileName + '"'
                    let ossHost = "//" + res.ossHost;
                    store.OSSEndPont = ossHost;
                    formData = formData || new FormData();
                    Object.keys(data).forEach(key => {
                        formData.append(key, data[key]);
                    })

                    let okurl = "/NSO"+res.key;
                    return this.post2(rawFile, formData, fileName, ossHost, okurl);
                })
            } else {
                return this.post2(rawFile, formData, fileName)
            }
        },
        post2: function (rawFile, formData, fileName, host, okurl) {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest()
                const action = host || this.server || store.UploadServer;

                var that = this;
                if (xhr.upload) {
                    xhr.upload.onprogress = function progress(e) {
                        if (e.total > 0) {
                            e.percent = parseFloat((e.loaded / e.total * 100).toFixed(2))
                            that.percent = e.percent;
                        }
                    }
                }
                that.percent = 0
                formData = formData || new FormData();

                if (this.extData && !this.ossServer) {
                    Object.keys(this.extData).forEach(key => {
                        formData.append(key, this.extData[key])
                    })
                }

                if (rawFile != null) {
                    formData.append("file", rawFile, rawFile.name);
                }


                xhr.onerror = function error() {
                    that.percent = 0
                    that.onError(xhr)
                    reject(xhr);
                }

                xhr.onload = function onload() {
                    if (xhr.status < 200 || xhr.status >= 300) {
                        that.onError(xhr)
                        reject(xhr);
                        return
                    }
                    var text = xhr.responseText || xhr.response
                    if(okurl){
                        text = { success:true, url: okurl, name:fileName};
                    }else{
                        try {
                            text = JSON.parse(text)
                        } catch {

                        }
                    }
                    that.percent = 0
                    if (text.success) {
                        that.$emit("uploadOK", { url: text.url, name: text.name || fileName, formData: that.formData });
                    }
                    that.onSuccess(text)
                    resolve(text);
                }

                xhr.open('post', action, true)

                if ('withCredentials' in xhr) {
                    xhr.withCredentials = false
                }

                const headers = that.headers || {}

                for (const item in headers) {
                    if (headers.hasOwnProperty(item) && headers[item] !== null) {
                        xhr.setRequestHeader(item, headers[item])
                    }
                }
                if(store.JWTToken){
                    xhr.setRequestHeader('Authorization',`Bearer ${store.JWTToken}`)
                }
                xhr.send(formData)
                return xhr
            });

        },
        onSuccess: function (res) {
            //在这里处理服务端返回的附件路径
            if (!res.success) {
                alert(res.message)
                return
            }
            if(res.data){
                res = res.data;
            }

            var item = this.modelValue || "";
            if (item.length > 0) {
                //安卓本地文件，上传以后，用服务器返回地址代替
                if (item.startsWith("/data/user/0")) {
                    item = "";
                } else {
                    item += ';'
                }
            }
            var url = res.url;
            if (url.endsWith(".thumb.jpg")) {
                url = url.substr(0, url.length - 10);
            }
            item += url;
            this.$emit('update:modelValue', item)


        },
        onError: function (err) {
            $msg.message(err, "error");
        },


        doUpload: function () {
            if (this.disabled) return;
            if (this.onlyCamera == true) {
                this.$refs.inputCameraRef.value = null;
                this.$refs.inputCameraRef.click();
            } else if (this.onlyVideo == true) {
                this.$refs.inputVideoRef.value = null;
                this.$refs.inputVideoRef.click();
            } else {
                this.$refs.inputRef.value = null;
                
                this.$refs.inputRef.click();
            }
        },
        /**
         * 格式化文件大小, 输出成带单位的字符串
         * @param {Number} size 文件大小
         * @param {Number} [pointLength=2] 精确到的小数点数。
         * @param {Array} [units=[ 'B', 'K', 'M', 'G', 'TB' ]] 单位数组。从字节，到千字节，一直往上指定。
         *    如果单位数组里面只指定了到了K(千字节)，同时文件大小大于M, 此方法的输出将还是显示成多少K.
         */
        formatSize(size, pointLength, units) {
            var unit;
            units = units || ['B', 'K', 'M', 'G', 'TB'];
            while ((unit = units.shift()) && size > 1024) {
                size = size / 1024;
            }
            return (unit === 'B' ? size : size.toFixed(pointLength === undefined ? 2 : pointLength)) + unit;
        },

    },
    computed: {
        photoList() {
            var lst = [];
            var ss = this.modelValue || "";
            ss.split(/[;,|\n]/g).filter(s => s != '').forEach(item => {
                lst.push(item)
            })
            return lst;

        },
        realPhoto() {
            var str = this.modelValue || "";
            if (str.length > 0) {
                return store.getUrl(str);
            }
            return "";
        },

        imgWidth() {
            return parseInt(this.imgSize);
        },
        maxSize2() {
            return Math.max(this.max, this.maxSize);
        },
        allowMulti(){
            return (this.max - this.photoList.length) > 1;
        }
    },
    watch: {
        modelValue(val) {
            //当传入的链接是android本机地址的时候，通过触发<input type="file" accept=".last">，请求andoird 上定制的webview ，返回最后一次需要传递到页面的文件
            if (val && val.startsWith("/data/user/0") && this.$refs.inputRef) {
                this.$refs.inputRef.accept = ".last";
                this.$refs.inputRef.value = null;
                this.$refs.inputRef.click();
            }
        }
    }
}
</script>

<style scoped>
.image-block {
    min-height: 30px;
    border: 1px dashed #eee;
}

.my-image-item {
    position: relative;
    display: inline-block;
    max-width: v-bind(imgSize);
    max-height: v-bind(imgSize);
    margin-right: 5px;
    margin-bottom: 5px;
    vertical-align: top;
}

img.my-image-item {
    max-width: v-bind(imgSize);
    max-height: v-bing(imgSize);
}

.pic-icon {
    justify-content: center;
    align-items: center;
    position: relative;

    display: inline-block;
    font-size: v-bind(imgSize) !important;
    border: 1px dashed #cecece;
    color: #cecece;
    width: v-bind(imgSize) !important;
    height: v-bind(imgSize) !important;
}

.img-remove-btn {
    display: block;
    position: absolute;
    top: 0;
    right: 0;
    width: 24px;
    height: 24px;
    font-size: 24px;
    font-weight: bold;
    color: red;
    cursor: pointer;
}

.single-img {
    width: v-bind(imgSize);
    height: v-bind(imgSize);
    overflow: hidden;
}

.percent {
    height: 4px;
    background-color: #001fff;
}

.content-delete {
    position: relative;
    display: block;
    background-color: transparent !important;
}

.filebox {
    position: relative;
    display: inline-block;
    width: 100%;
    max-height: 80% !important;
    text-align: center !important;
}
</style>